import { ipcMain } from "electron";
import { createHash } from 'node:crypto'
export const CHROMIUM_FULL_VERSION = '130.0.2849.68'
export const TRUSTED_CLIENT_TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'
const WINDOWS_FILE_TIME_EPOCH = 11644473600n
import { randomBytes } from 'node:crypto';
import CryptoJS from "crypto-js";
const WebSocket = require("ws")
const fs = require('fs');
import path from 'path'
let saveFile: any = "";
// 相关资料
// https://github.com/lobehub/lobe-tts/blob/master/src/core/EdgeSpeechTTS/createEdgeSpeech.ts

function generateSecMsGecToken() {
  const ticks = BigInt(Math.floor((Date.now() / 1000) + Number(WINDOWS_FILE_TIME_EPOCH))) * 10000000n
  const roundedTicks = ticks - (ticks % 3000000000n)

  const strToHash = `${roundedTicks}${TRUSTED_CLIENT_TOKEN}`

  const hash = createHash('sha256')
  hash.update(strToHash, 'ascii')

  return hash.digest('hex').toUpperCase()
}

function getVoiceList() {
  let list: any = [
    { label: '英(英式)', value: 'en-GB', list: [] },
    { label: '英(美式)', value: 'en-US', list: [] },
    { label: '中文', value: 'zh-CN', list: [] },
  ];
  const voices: any = [
    { lable: "晓晓", value: "zh-CN-XiaoxiaoNeural" },
    { lable: "晓依", value: "zh-CN-XiaoyiNeural" },
    { lable: "云嘉", value: "zh-CN-YunjianNeural" },
    { lable: "云溪", value: "zh-CN-YunxiNeural" },
    { lable: "云夏", value: "zh-CN-YunxiaNeural" },
    { lable: "云阳", value: "zh-CN-YunyangNeural" },
    { lable: "艾娃(多人)", value: "en-US-AvaMultilingualNeural" },
    { lable: "安德鲁(多人)", value: "en-US-AndrewMultilingualNeural" },
    { lable: "艾玛(多人)", value: "en-US-EmmaMultilingualNeural" },
    { lable: "布莱恩(多人)", value: "en-US-BrianMultilingualNeural" },
    { lable: "艾娃", value: "en-US-AvaNeural" },
    { lable: "安德鲁", value: "en-US-AndrewNeural" },
    { lable: "艾玛", value: "en-US-EmmaNeural" },
    { lable: "布莱恩", value: "en-US-BrianNeural" },
    { lable: "莉比", value: "en-GB-LibbyNeural" },
    { lable: "梅齐", value: "en-GB-MaisieNeural" },
    { lable: "瑞安", value: "en-GB-RyanNeural" },
    { lable: "索尼亚", value: "en-GB-SoniaNeural" },
    { lable: "托马斯", value: "en-GB-ThomasNeural" },
    { lable: "安娜", value: "en-US-AnaNeural" },
    { lable: "阿里亚", value: "en-US-AriaNeural" },
    { lable: "克里斯多夫", value: "en-US-ChristopherNeural" },
    { lable: "艾瑞克", value: "en-US-EricNeural" },
    { lable: "盖伊", value: "en-US-GuyNeural" },
    { lable: "詹妮", value: "en-US-JennyNeural" },
    { lable: "米歇尔", value: "en-US-MichelleNeural" },
    { lable: "罗杰", value: "en-US-RogerNeural" },
    { lable: "斯特凡", value: "en-US-SteffanNeural" }
  ]
  const categories: any = {
    News: '新闻',
    Novel: '小说',
    Cartoon: '卡通',
    Sports: '娱乐',
    Conversation: '自由交谈',
    Copilot: '客服',
    General: '普通',
  }

  const Persona: any = {
    "Warm": "温暖的",
    "Lively": "活泼的",
    "Passion": "激情的",
    "Sunshine": "阳光的",
    "Cute": "可爱的",
    "Professional": "专业的",
    "Reliable": "可信赖的",
    "Expressive": "富于表情的",
    "Caring": "充满关爱",
    "Pleasant": "愉快的",
    "Friendly": "亲切的",
    "Confident": "自信的",
    "Authentic": "传统的",
    "Honest": "坦诚直率",
    "Cheerful": "欢快开朗",
    "Clear": "清晰晴朗",
    "Conversational": "口语的",
    "Approachable": "亲切友善的",
    "Casual": "休闲的",
    "Sincere": "真诚的",
    "Positive": "乐观的",
    "Authority": "专家",
    "Rational": "理智的",
    "Considerate": "体贴谨慎",
    "Comfort": "舒服舒适",
  }
  return fetch('https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4').then(data => data.json()).then(data => {
    data.filter(v => ['en-GB', 'en-US', 'zh-CN'].includes(v.Locale)).forEach(item => {
      let curr = list.find(f => f.value == item.Locale);
      curr.list.push({
        name: (voices.find(v => v.value == item.ShortName)?.lable || '无名') + `(${item.Gender == 'Female' ? '女' : '男'})`,
        sex: item.Gender == 'Female' ? '女' : '男',
        value: item.ShortName,
        contentCategories: item.VoiceTag.ContentCategories.map(v => { return { value: v.trim(), label: categories[v.trim()] } }),
        voicePersonalities: item.VoiceTag.VoicePersonalities.map(v => { return { value: v.trim(), label: Persona[v.trim()] } }),
        lang: curr.label
      })
    })
    return list
  })
}


function setXmlText(data: any) {
  let requestId = randomBytes(16).toString('hex')
  return `
  X-RequestId:${requestId}\r\nContent-Type:application/ssml+xml\r\n
  X-Timestamp:${new Date()}\r\nPath:ssml\r\n\r\n
  <speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xmlns:mstts='https://www.w3.org/2001/mstts' xml:lang='${data.lang}'>
    <voice name='${data.name}'>
        <prosody pitch='${data.pitch}' rate ='${data.rate}' volume='${data.volume}'>${data.message}</prosody>
    </voice>
  </speak>
  `;
}


function initTts(vdata, sentence, word) {
  let bufferList: any = [];
  let buffer: any = [];
  return new Promise((resolve, reject) => {
    let ws = new WebSocket(`wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${generateSecMsGecToken()}&Sec-MS-GEC-Version=1-${CHROMIUM_FULL_VERSION}`, {
      host: 'speech.platform.bing.com',
      origin: 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',
      },
      agent: undefined
    })
    //  "outputFormat": "audio-24khz-48kbitrate-mono-mp3"
    ws.on("open", () => {
      ws.send(`Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n
      {
        "context": {
          "synthesis": {
            "audio": {
              "metadataoptions": {
                "sentenceBoundaryEnabled": ${sentence},
                "wordBoundaryEnabled": ${word}
              },
              "outputFormat": "webm-24khz-16bit-mono-opus"
            }
          }
        }
      }
    `)
      setTimeout(() => {
        ws.send(setXmlText(vdata))
      }, 100);
    })

    ws.on("message", (data, isBrinary) => {

      if (isBrinary) {
        let separator = "Path:audio\r\n";
        let contentIndex = data.indexOf(separator) + separator.length;
        bufferList.push(data.slice(contentIndex))
      } else {
        if (data.toString().includes('audio.metadata')) {
          let separator = "audio.metadata\r\n";
          let contentIndex = data.indexOf(separator) + separator.length;
          buffer.push(data.slice(contentIndex).toString())
        }
        if (data.toString().includes("Path:turn.end")) {
          ws.close()
        }
      }
    })


    // My Bicycle . I have a bicycle. It is red and has two wheels. I ride my bicycle to school every day. It is fun and good exercise. My friends also have bicycles. We ride together in the park. I love my bicycle very much.

    ws.on('close', async e => {
      let fanyi = await baiduTranslate(vdata.message);
      if (bufferList.length && saveFile) {
        let saveTTS = {
          bufferList,
          buffer,
          fanyi
        }
        //自由朗读生的短文 需要标题翻译
        if (saveFile.includes('=')) {
          fs.writeFile(`${saveFile}_${fanyi.split('。')[0]}.json`, JSON.stringify(saveTTS), e => { })
        } else {
          fs.writeFile(`${saveFile}.json`, JSON.stringify(saveTTS), e => { })
        }
      }
      resolve({ bufferList, buffer, fanyi })
    })

    ws.on("error", e => {
      reject('连接失败  connection fail')
    })
  })
}

function baiduTranslate(text, from = 'en', to = 'zh') {
  const salt = Date.now().toString();

  // 计算签名（sign = md5(appId + text + salt + appKey)）
  const signStr = '20241204002218658' + text + salt + 'LjV3TABzkLOUD87dBFMI';
  const sign = CryptoJS.MD5(signStr).toString(); // 需要实现 MD5 计算

  const url = "http://api.fanyi.baidu.com/api/trans/vip/translate";
  const params = new URLSearchParams();
  params.append("q", text);
  params.append("from", from);
  params.append("to", to);
  params.append("appid", '20241204002218658');
  params.append("salt", salt);
  params.append("sign", sign);

  return fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: params,
  }).then(data => data.json()).then(res => {
    return res.trans_result[0].dst
  })
}

ipcMain.handle('tts-speek', async (e, data) => {
  if (data.isFile) {
    let dir = path.join(data.file.fileUrl, data.fileName && (data.fileName + '\\') || '');
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    saveFile = `${dir}${data.file.title}-${data.lang}-${data.rate}-${data.name}`;
    // 自由朗读生的短文
    if (data.fileName) {
      saveFile = saveFile + `=${new Date().getTime()}`;
    }
    if (fs.existsSync(`${saveFile}.json`)) {
      const relativePath = path.resolve(saveFile);
      const res = JSON.parse(fs.readFileSync(`${relativePath}.json`, 'utf8'));
      res.bufferList = res.bufferList.map(v => new Uint8Array(v.data))
      return res;
    }
  } else {
    saveFile = "";
  }

  let vdata = {
    name: data.name,
    lang: data.lang,
    outputFormat: data.outputFormat || 'audio-24khz-48kbitrate-mono-mp3',
    rate: setPitch(data.rate),
    pitch: setPitch(data.pitch),
    volume: setVolume(data.volume),
    message: data.message,
  }

  return initTts(vdata, data.sentence || data.line || false, data.word || false);
})

ipcMain.handle('get-history-tts', async (e, data) => {
  if (fs.existsSync(data.path)) {
    const res = JSON.parse(fs.readFileSync(path.resolve(data.path), 'utf8'));
    res.bufferList = res.bufferList.map(v => new Uint8Array(v.data))
    return res;
  }
});

ipcMain.handle('get-tts-file', async (e, data) => {
  let dir = getAllFilesSync(path.resolve(data.path || ''));
  return dir;
})

function getAllFilesSync(dir, index: number = 0) {
  let results: any = [];
  try {
    const list = fs.readdirSync(dir);

    list.forEach(file => {
      const filePath = path.join(dir, file);
      const stats = fs.statSync(filePath);
      if (stats.isDirectory()) {
        results.push(...getAllFilesSync(filePath, index + 1)); // 递归合并
      } else {
        if (index > 0) {
          results.push({
            title: file.replace(/-.+/g, ''),
            cn: file.replace(/\w.+_|\..+/g, ''),
            creatime: file.replace(/\w.+=|_.+/g, ''),
            dir: dir.split('\\').pop(),
            fileUrl: path.resolve(filePath)
          });
        }
      }
    });
  } catch (err) {
    console.error(`读取失败: ${dir}`, err);
  }
  return results;
}

function toTts(vdata) {
  let bufferList: any = [];
  let buffer: any = [];
  return new Promise((resolve, reject) => {
    let ws = new WebSocket(`wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1?TrustedClientToken=${TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${generateSecMsGecToken()}&Sec-MS-GEC-Version=1-${CHROMIUM_FULL_VERSION}`, {
      host: 'speech.platform.bing.com',
      origin: 'chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold',
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',
      },
      agent: undefined
    })
    //  "outputFormat": "audio-24khz-48kbitrate-mono-mp3"
    ws.on("open", () => {
      ws.send(`Content-Type:application/json; charset=utf-8\r\nPath:speech.config\r\n\r\n
      {
        "context": {
          "synthesis": {
            "audio": {
              "metadataoptions": {
                "sentenceBoundaryEnabled": false,
                "wordBoundaryEnabled": false
              },
              "outputFormat": "webm-24khz-16bit-mono-opus"
            }
          }
        }
      }
    `)
      setTimeout(() => {
        ws.send(setXmlText(vdata))
      }, 100);
    })

    ws.on("message", (data, isBrinary) => {

      if (isBrinary) {
        let separator = "Path:audio\r\n";
        let contentIndex = data.indexOf(separator) + separator.length;
        bufferList.push(data.slice(contentIndex))
      } else {
        if (data.toString().includes("Path:turn.end")) {
          ws.close()
        }
      }
    })

    ws.on('close', async e => {
      resolve(bufferList.map(v => new Uint8Array(v)))
    })

    ws.on("error", e => {
      reject('连接失败  connection fail')
    })
  })
}

// 语音合成 不需要生成文件
ipcMain.handle('toSpeek', async (e, data) => {
  let vdata = {
    name: data.name,
    lang: data.lang,
    outputFormat: data.outputFormat || 'audio-24khz-48kbitrate-mono-mp3',
    rate: setPitch(data.rate),
    pitch: setPitch(data.pitch),
    volume: setVolume(data.volume),
    message: data.message,
  }
  return toTts(vdata)
})

function setPitch(value) {
  if (value == 1) {
    return "medium";
  }
  if (value > 1) {
    return `+${Number(String(value).split('.')[1] || 0) || 100}%`
  }
  return `-${100 - Number(String(value).split('.')[1] || 0)}%`;
}

function setVolume(value) {
  if (value == 1) {
    return "x-loud";
  }
  if (value > 1) {
    return `+${Number(String(value).split('.')[1] || 0) || 100}%`
  }
  return `${100 - Number(String(value).split('.')[1] || 0)}%`;
}

ipcMain.handle('getVoice', async (e, data) => {
  return getVoiceList()
})
